package com.xw.unification.entity.request;

import com.xw.unification.exception.RequestBodyOverLengthException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Optional;

/**
 * @author qiwei
 * @since v1.0.0
 */
public class CopyOnReadHttpServletRequest extends RepeatableHttpServletRequest {

    private static final Logger LOGGER = LoggerFactory.getLogger(CopyOnReadHttpServletRequest.class);

    private byte[] data;

    public CopyOnReadHttpServletRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
        long length = request.getContentLengthLong();
        if (length > Integer.MAX_VALUE) {
            LOGGER.error("Request body over length, content length: {} > Integer.MAX_VALUE", length);
            throw new RequestBodyOverLengthException(HttpStatus.INTERNAL_SERVER_ERROR.value(),
                    "Request body over length, content length: " + length, null);
        } else if (length > 0) {
            LOGGER.info("Request body content length: {} <= Integer.MAX_VALUE, do overwrite", length);
        } else {
            LOGGER.info("Request body content length: {}, no overwrite", length);
            data = new byte[]{};
            return;
        }
        ServletInputStream input = null;
        try {
            input = request.getInputStream();
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buffer = new byte[ServletInputStreamWrapper.DEFAULT_BUFFER_SIZE];
            long count = 0;
            int n = 0;
            while ((n = input.read(buffer)) > 0) {
                output.write(buffer, 0, n);
                count += n;
            }
            if (count > Integer.MAX_VALUE) {
                LOGGER.error("Request body over length in stream reading");
                throw new RequestBodyOverLengthException(HttpStatus.INTERNAL_SERVER_ERROR.value(),
                        "Request body over length in stream reading", null);
            }
            this.data = output.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("Failed to wrap the input stream of HttpServletRequest", e);
        }
    }

    @Override
    protected ServletInputStreamWrapper getInputStreamWrapper() throws IOException {
        return new ServletInputStreamWrapper(this);
    }

    byte[] getData() {
        return data;
    }

    private static HttpServletRequest checkRequest(HttpServletRequest request) {
        if (request == null) {
            throw new IllegalArgumentException("HttpServletRequest can not be null");
        }
        return request;
    }

    void rewrite(byte[] data) {
        this.data = Optional.ofNullable(data).orElse(new byte[0]);
    }

}
